// ------------------------------------------------------------------------------
// 
// Copyright (c) 2008 Swampware, Inc.
// 
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// 
// ------------------------------------------------------------------------------
using System;

namespace Water.Commands
{
	/// <summary>
	/// Summary description for ParallelCommand.
	/// </summary>
	public class ParallelCommand : Water.Commands.Command
	{

		public ParallelCommand()
		{
		}

		public override string Name
		{
			get { return "parallel"; }
		}

		public override object Evaluate(Water.List expressions, Water.Environment environment, Water.Evaluator evaluator)
		{
			if(expressions.Count == 1)
			{
				Water.List tasks = CreateTasks((Water.List)evaluator.Evaluate(expressions[0], environment), evaluator, environment);

				RunTasks(tasks, tasks.Count);
				CheckErrors(tasks);
				return GetResults(tasks);
			}
			else if(expressions.Count == 2)
			{
				Water.List tasks = CreateTasks((Water.List)evaluator.Evaluate(expressions[0], environment), evaluator, environment);
				int threadCount = (int)evaluator.Evaluate(expressions[1], environment);

				RunTasks(tasks, threadCount);
				CheckErrors(tasks);
				return GetResults(tasks);
			}
			else
			{
				throw new Water.Commands.InvalidArgumentsException(this.Name, expressions);
			}
		}

		private static Water.List CreateTasks(Water.List list, Water.Evaluator evaluator, Water.Environment environment)
		{
			Water.List tasks = new Water.List();
			foreach(Water.List task in list)
			{
				tasks.Add(new ParallelTask(task, environment, evaluator));
			}
			return tasks;
		}

		private static void RunTasks(Water.List tasks, int threadCount)
		{
			System.Collections.ArrayList threads = new System.Collections.ArrayList();
			foreach (ParallelTask task in tasks)
			{
				System.Threading.ThreadStart threadStart = new System.Threading.ThreadStart(task.Execute);
				System.Threading.Thread thread = new System.Threading.Thread(threadStart);
				threads.Add(thread);
				thread.Start();
			}
			foreach(System.Threading.Thread thread in threads)
			{
				thread.Join();
			}
		}

		private static void CheckErrors(Water.List tasks)
		{
			Water.List errors = new Water.List();
			foreach (ParallelTask task in tasks)
			{
				if(task.Error != null)
				{
					errors.Add(task.Error);
				}
			}

			if(errors.Count > 0)
			{
				throw new ParallelException(errors);
			}
		}

		private static Water.List GetResults(Water.List tasks)
		{
			bool hasResults = false;
			Water.List results = new Water.List();
			foreach (ParallelTask task in tasks)
			{
				if(task.Result != null)
				{
					hasResults = true;
				}
				results.Add(task.Result);
			}
			if(hasResults)
			{
				return results;
			}
			else
			{
				return null;
			}
		}

	}
}
